﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
using System.Text.RegularExpressions;
using System.Reflection;
using Microsoft.Contracts;

namespace ObjectBakery.Helpers
{
    public class ObjectHelper
    {
        public const string breakMarker = "---BREAK---";

        public static string GenerateKey()
        {
            // Create an instance of Symetric Algorithm. Key and IV is generated automatically.
            DESCryptoServiceProvider desCrypto = (DESCryptoServiceProvider)DESCryptoServiceProvider.Create();

            // Use the Automatically generated key for Encryption. 
            return ASCIIEncoding.ASCII.GetString(desCrypto.Key);
        }

        public Dictionary<string, string> GetFileKeyData(string fileText)
        {
            Dictionary<string, string> manifestData = null;
            if (File.Exists(fileText))
            {

                if (!fileText.Equals(string.Empty))
                {
                    fileText = File.ReadAllText(fileText);
                    manifestData = new Dictionary<string, string>();
                    StringReader sr = new StringReader(fileText);
                    Regex splitter = new Regex(breakMarker);
                    while (true)
                    {
                        string[] line = splitter.Split(sr.ReadLine());
                        if (line != null && line.Length > 1)
                        {
                            manifestData.Add(line[0], line[1]);
                        }
                        if (sr.Peek() == -1)
                            break;
                    }
                }
            }

            return manifestData;
        }

        public bool EncryptObjectToDisk(string fileFullPath, object data, string eKey)
        {
            FileStream fsEncrypted = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write);
            bool saveSuccess = false;
            try
            {
                DESCryptoServiceProvider des = new DESCryptoServiceProvider();
                des.Key = ASCIIEncoding.ASCII.GetBytes(eKey);
                des.IV = ASCIIEncoding.ASCII.GetBytes(eKey);
                CryptoStream cryptostream = new CryptoStream(fsEncrypted, des.CreateEncryptor(), CryptoStreamMode.Write);
                BinaryFormatter bin = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File));
                using (StreamWriter streamWriter = new StreamWriter(cryptostream))
                {
                    bin.Serialize(streamWriter.BaseStream, data);
                    streamWriter.Flush();
                    streamWriter.Close();
                    saveSuccess = true;
                }
            }
            finally
            {
                if (fsEncrypted != null)
                    fsEncrypted.Close();
            }
            return saveSuccess;
        }

        public object DecryptObjectFromDisk(string fileFullPath, string eKey)
        {
            if (!File.Exists(fileFullPath))
                return null;
            FileStream fsEncrypted = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read);
            try
            {
                DESCryptoServiceProvider des = new DESCryptoServiceProvider();
                des.Key = ASCIIEncoding.ASCII.GetBytes(eKey);
                des.IV = ASCIIEncoding.ASCII.GetBytes(eKey);
                CryptoStream cryptostream = new CryptoStream(fsEncrypted, des.CreateDecryptor(), CryptoStreamMode.Read);
                object data = null;
                BinaryFormatter bin = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File));
                using (StreamReader reader = new StreamReader(cryptostream))
                {
                    data = bin.Deserialize(reader.BaseStream);
                    reader.Close();
                }
                return data;
            }
            finally
            {
                if (fsEncrypted != null)
                    fsEncrypted.Close();
            }
        }


        public T CreateObject<T>()
        {
            Type t = typeof(T);
           
            if (t != null)
            {
                return (T)Activator.CreateInstance(t);
            }
            else
                return default(T);
        }


        public T CreateObject<T>(string fullyQualifiedPathToClass)
        {
            var type = typeof (T);
            return (T) CreateObject(type, fullyQualifiedPathToClass);
        }

        public object CreateObject(Type targetedType, string fullyQualifiedPathToClass)
        {
            //fish out type def
            Type t = Type.GetType(fullyQualifiedPathToClass);
            if (t == null)
            {
                //    try
                //    {
                //        t = Assembly.GetCallingAssembly().GetType(fullyQualifiedPathToClass);
                //    }
                //    catch
                //    {
                //    }
                //if (t == null) //still?
                //    try
                //    {
                //        t = Assembly.GetEntryAssembly().GetType(fullyQualifiedPathToClass);
                //    }
                //    catch
                //    {
                //    }
                //if (t == null) //still?
                //    try
                //    {
                //        t = Assembly.GetExecutingAssembly().GetType(fullyQualifiedPathToClass);
                //    }
                //    catch
                //    {
                //    }

                foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
                {
                    t = assembly.GetType(fullyQualifiedPathToClass);
                    if (t != null)
                        break;
                }


                if (t != null &&  t == targetedType)
                {
                    return Activator.CreateInstance(t);
                }
                else
                    //return default(T);
                    throw new ApplicationException("Cannot find type specified in assemblies loaded into AppDomain");
            }
            else
            {
                throw new ArgumentException("The string argument passed to this method does not match the type specified");
            }
        }

        public string GetFullyQualifiedPathToType(Type type)
        {
            string path = type.AssemblyQualifiedName.Split(',')[0];
            if (path == null)
                path = "";

            return path;
        }
    }
}
